package com.linln.admin.app.service.branch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class BranchHelper {
    private static final List<BranchHandler> handlers = new ArrayList<>();

    static {
        regiestHandler(new JGitBranchHandler());
    }

    public static boolean regiestHandler(BranchHandler handler) {
        if (handler == null) {
            return false;
        }
        handlers.add(handler);
        return true;
    }

    public static BranchHandler findHandler(String type) {
        for (BranchHandler handler : handlers) {
            if (handler.support(type)) {
                return handler;
            }
        }
        return null;
    }

    /**
     * 提交记录
     */
    public static class CommitLog {
        private String commitId;
        private String user;
        private String email;
        private Date commitTime;

        public String getCommitId() {
            return commitId;
        }

        public void setCommitId(String commitId) {
            this.commitId = commitId;
        }

        public String getUser() {
            return user;
        }

        public void setUser(String user) {
            this.user = user;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public Date getCommitTime() {
            return commitTime;
        }

        public void setCommitTime(Date commitTime) {
            this.commitTime = commitTime;
        }
    }

    public static abstract class BranchHandler {
        private static final Logger log = LoggerFactory.getLogger(BranchHandler.class);

        public abstract boolean support(String type);

        /**
         * 获取指定应用的所有分支及对应的提交信息
         *
         * @param tmpWorkDir 项目临时目录名称
         * @param url        项目分支地址
         * @param account    获取分支代码的账号
         * @param pass       获取分支代码的密码
         * @return 返回对应分支及最后一次提交日志
         * @throws Exception 获取分支信息失败抛出异常
         */
        public abstract Map<String, CommitLog> branchList(String tmpWorkDir, String url, String account, String pass) throws Exception;

        /**
         * 创建一个新分支，指定从哪个分支为基准
         *
         * @param url  主干url或来源url
         * @param orig 源分支
         * @param dest 目标分支
         * @return 成功-true；其它-false
         */
        public abstract boolean createNewBranch(String tmpWorkDir, String url, String account, String pass, String orig, String dest) throws Exception;

        /**
         * 合并两个分支代码
         *
         * @param url  主干url或来源url
         * @param orig 源分支
         * @param dest 目标分支
         * @return 成功-true；其它-false
         */
        public abstract boolean mergeBranch(String tmpWorkDir, String url, String account, String pass, String orig, String dest) throws Exception;

        /**
         * 检出指定分支的代码
         *
         * @param tmpWorkDir 项目临时目录名称
         * @param url        项目分支地址
         * @param account    获取分支代码的账号
         * @param pass       获取分支代码的密码
         * @param branch     指定的分支名称
         * @return 返回代码所在目录
         * @throws Exception 操作失败时抛出异常
         */
        public abstract File checkoutBranch(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception;

        /**
         * 删除指定分支
         *
         * @param tmpWorkDir 项目临时目录名称
         * @param url        项目分支地址
         * @param account    获取分支代码的账号
         * @param pass       获取分支代码的密码
         * @param branch     指定的分支名称
         * @return 返回操作结果
         * @throws Exception 操作失败时抛出异常
         */
        public abstract boolean deleteBranch(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception;


        /**
         * 执行系统命令
         *
         * @param cmd           命令内容
         * @param tmpProjectDir 指定命令在哪个目录下执行
         * @throws IOException          抛出IO异常
         * @throws InterruptedException 抛出线程异常
         */
        public static int execCmd(String cmd, File tmpProjectDir) throws IOException, InterruptedException {
            return execCmd(cmd, tmpProjectDir, new StringBuilder());
        }

        public static int execCmd(String cmd, File tmpProjectDir, StringBuilder respose) throws IOException, InterruptedException {
            Process process = Runtime.getRuntime().exec(cmd, null, tmpProjectDir);
            readResponse(process, respose);
            return process.waitFor();
        }

        private static void readResponse(final Process process, final StringBuilder result) {
            new Thread(() -> {
                BufferedReader in = null;
                String line;
                try {
                    in = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    while ((line = in.readLine()) != null) {
                        result.append(line);
                        result.append("\n");
                    }
                } catch (Exception e) {
                    log.warn(e.getMessage(), e);
                } finally {
                    try {
                        in.close();
                    } catch (Exception ignore) {
                    }
                }
            }).start();

            new Thread(() -> {
                BufferedReader err = null;
                String line;
                try {
                    err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                    while ((line = err.readLine()) != null) {
//                    result.append(line);
                        System.err.println(line);
                    }
                } catch (Exception e) {
                    log.warn(e.getMessage(), e);
                } finally {
                    try {
                        err.close();
                    } catch (Exception ignore) {
                    }
                }
            }).start();
        }
    }
}
